package snuvm.arm9.instructions;

import snuvm.simulator.*;
import snuvm.arm9.hardware.*;
import snuvm.arm9.instructions.syscalls.*;
import snuvm.*;
import snuvm.util.*;
import java.util.*;
import java.io.*;
	
/*
Copyright (c) 2009-2010,  Jeongho Nah, Gangwon Jo, Jaejin Lee

Developed by 
	Jeongho Nah (jeongho"at"aces.snu.ac.kr)
	Gangwon Jo (gangwon"at"aces.snu.ac.kr)
	Jaejin Lee (jlee"at"cse.snu.ac.kr)


Permission is hereby granted to use this version of the code 
under the same terms as MARS, which has the following license:

--
Copyright (c) 2003-2008,  Pete Sanderson and Kenneth Vollmar

Developed by Pete Sanderson (psanderson"at"otterbein.edu)
and Kenneth Vollmar (kenvollmar"at"missouristate.edu)

Permission is hereby granted, free of charge, to any person obtaining 
a copy of this software and associated documentation files (the 
"Software"), to deal in the Software without restriction, including 
without limitation the rights to use, copy, modify, merge, publish, 
distribute, sublicense, and/or sell copies of the Software, and to 
permit persons to whom the Software is furnished to do so, subject 
to the following conditions:

The above copyright notice and this permission notice shall be 
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

(MIT license, http://www.opensource.org/licenses/mit-license.html)
 */

/**
 * The list of Instruction objects, each of which represents a MIPS instruction.
 * The instruction may either be basic (translates into binary machine code) or
 * extended (translates into sequence of one or more basic instructions).
 *
 * @author Pete Sanderson and Ken Vollmar
 * @version August 2003-5
 */

    public class InstructionSet
   {
      private ArrayList instructionList;
      private SyscallLoader syscallLoader;
    /**
     * Creates a new InstructionSet object.
     */
       public InstructionSet()
      {
         instructionList = new ArrayList();
      
      }
    /**
     * Retrieve the current instruction set.
     */
       public ArrayList getInstructionList()
      {
         return instructionList;
      
      }
    /**
     * Adds all instructions to the set.  A given extended instruction may have
     * more than one Instruction object, depending on how many formats it can have.
     * @see Instruction
     * @see BasicInstruction
     * @see ExtendedInstruction
     */
       public void populate()
      {
        /* Here is where the parade begins.  Every instruction is added to the set here.*/
      
        // ////////////////////////////////////   BASIC INSTRUCTIONS START HERE ////////////////////////////////
      
         instructionList.add(
                 new BasicInstruction("add r0,r1,#-128",
             	 "Addition immediate, #<immed8>",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 001 0100 0 ssss ffff tttttttttttt",
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      int add1 = RegisterFile.getValue(operands[1]);
                      int add2 = operands[2];
                      int sum = add1 + add2;
                   // overflow on A+B detected when A and B have same sign and A+B has other sign.
                      if ((add1 >= 0 && add2 >= 0 && sum < 0)
                         || (add1 < 0 && add2 < 0 && sum >= 0))
                      {
                         throw new ProcessingException(statement,
                             "arithmetic overflow",Exceptions.ARITHMETIC_OVERFLOW_EXCEPTION);
                      }
                      RegisterFile.updateRegister(operands[0], sum);
                   }
                }));
         instructionList.add(
                new BasicInstruction("add r0,r1,r2",
            	 "Addition",
            	 BasicInstructionFormat.I_FORMAT,
                 "1110 000 0100 0 ssss ffff tttttttttttt",
                new SimulationCode()
               {
                   public void simulate(ProgramStatement statement) throws ProcessingException
                  {
                     int[] operands = statement.getOperands();
                     int add1 = RegisterFile.getValue(operands[1]);
                     int add2 = RegisterFile.getValue(operands[2]);
                     int sum = add1 + add2;
                  // overflow on A+B detected when A and B have same sign and A+B has other sign.
                     if ((add1 >= 0 && add2 >= 0 && sum < 0)
                        || (add1 < 0 && add2 < 0 && sum >= 0))
                     {
                        throw new ProcessingException(statement,
                            "arithmetic overflow",Exceptions.ARITHMETIC_OVERFLOW_EXCEPTION);
                     }
                     RegisterFile.updateRegister(operands[0], sum);
                  }
               }));
          instructionList.add(
                  new BasicInstruction("sub r0,r1,#-128",
              	 "Subtraction immediate, #<immed8>",
              	BasicInstructionFormat.I_FORMAT,
                "1110 001 0010 0 ssss ffff tttttttttttt",
                  new SimulationCode()
                 {
                 	 public void simulate(ProgramStatement statement) throws ProcessingException
                      {
                         int[] operands = statement.getOperands();
                         int sub1 = RegisterFile.getValue(operands[1]);
                         int sub2 = operands[2];
                         int dif = sub1 - sub2;
                      // overflow on A-B detected when A and B have opposite signs and A-B has B's sign
                         if ((sub1 >= 0 && sub2 < 0 && dif < 0)
                            || (sub1 < 0 && sub2 >= 0 && dif >= 0))
                         {
                            throw new ProcessingException(statement,
                                "arithmetic overflow",Exceptions.ARITHMETIC_OVERFLOW_EXCEPTION);
                         }
                         RegisterFile.updateRegister(operands[0], dif);
                      }
                 }));
         instructionList.add(
                 new BasicInstruction("sub r0,r1,r2",
             	 "Subtraction",
             	BasicInstructionFormat.I_FORMAT,
                "1110 000 0010 0 ssss ffff tttttttttttt",
                 new SimulationCode()
                 {
                     public void simulate(ProgramStatement statement) throws ProcessingException
                    {
                       int[] operands = statement.getOperands();
                       int sub1 = RegisterFile.getValue(operands[1]);
                       int sub2 = RegisterFile.getValue(operands[2]);
                       int dif = sub1 - sub2;
                    // overflow on A-B detected when A and B have opposite signs and A-B has B's sign
                       if ((sub1 >= 0 && sub2 < 0 && dif < 0)
                          || (sub1 < 0 && sub2 >= 0 && dif >= 0))
                       {
                          throw new ProcessingException(statement,
                              "arithmetic overflow",Exceptions.ARITHMETIC_OVERFLOW_EXCEPTION);
                       }
                       RegisterFile.updateRegister(operands[0], dif);
                    }
                 }));
         instructionList.add(
                 new BasicInstruction("mul r0,r1,r2",
             	 "Multiply",
             	BasicInstructionFormat.I_FORMAT,
                "1110 0000000 0 ffff 0000 tttt 1001 ssss",
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      int add1 = RegisterFile.getValue(operands[1]);
                      int add2 = RegisterFile.getValue(operands[2]);
                      int mvalue = add1 * add2;
                   // overflow on A+B detected when A and B have same sign and A+B has other sign.
                      if ((add1 >= 0 && add2 >= 0 && mvalue < 0)
                         || (add1 < 0 && add2 < 0 && mvalue >= 0))
                      {
                         throw new ProcessingException(statement,
                             "arithmetic overflow",Exceptions.ARITHMETIC_OVERFLOW_EXCEPTION);
                      }
                      RegisterFile.updateRegister(operands[0], mvalue);
                   }
                }));
         instructionList.add(
                 new BasicInstruction("mov r0,#-128",
                 "Move <Rd> <immed_8>",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 00 1 1101 0 0000 ffff ssssssssssss",
               // 1110 00 1 1101 0 0000 0011 000000010000
               // mov	r3, #16	@0x10
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   { 
                      int[] operands = statement.getOperands();
                      int operand1 = operands[1];
                      if (operands[0] == 15) {
                    	  processJump(operand1);
                      }
                      else {
                    	  RegisterFile.updateRegister(operands[0], operand1);
                      }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("mov r0,r1",
                 "Move <Rd> <Operand>",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 00 0 1101 0 0000 ffff ssss ssss ssss",
                //1110 00 1 1101 0 0000 0011000000010000
                
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   { 
                      int[] operands = statement.getOperands();
 
                      if (operands[0] == 15) {                    	  
                    	  int branchAddress = (RegisterFile.getValue(operands[1]) - (RegisterFile.getProgramCounter()))/4 - 1; // ARM9 pc + 1*4
                    	  processBranch(branchAddress);
                      }
                      else {
                    	  RegisterFile.updateRegister(operands[0], RegisterFile.getValue(operands[1]));
                      }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("mov r0,r1,LSL#31",
                 "Logical shift #<immed5>. Shift Type: LSL,LSR,ASR,ROR",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 00 0 1101 0 0000 ffff uuuuu tt0 ssss",
               // 1110 00 0 1101 0 0000 0000 00011 100 0001
               // mov	r0, r1, asr #3
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   { 
                    	int[] operands = statement.getOperands();
                    	
                    	if(operands[2]==0) { // LSL
                    		RegisterFile.updateRegister(operands[0],
                    				RegisterFile.getValue(operands[1]) << operands[3]);
                    	}
                    	else if(operands[2]==1) { // LSR
                    		RegisterFile.updateRegister(operands[0],
                    				RegisterFile.getValue(operands[1]) >>> operands[3]);
                    	}
                    	else if(operands[2]==2) { // ASR
                    		RegisterFile.updateRegister(operands[0],
                    				RegisterFile.getValue(operands[1]) >> operands[3]);
                    	}
                    	else if(operands[2]==3) { // ROR
                    		RegisterFile.updateRegister(operands[0],
                    				((RegisterFile.getValue(operands[1]) >>> operands[3]) | 
                    						RegisterFile.getValue(operands[1]) << (32-operands[3])));
                    	}
                    	else {
                    	}
                   }
                }));
         instructionList.add(
                 new BasicInstruction("mov r0,r1,RRX",
                 "Rotate right with extend (33 bits rotate with old carry)",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 000 1101 0 0000 ffff 00000 tt0 ssss",
               // 1110 000 1101 0 0000 0000 00000 110 0001
               // mov	r0, r1, asr #3
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   { 
                    	int[] operands = statement.getOperands();
                   		int csprNum = RegisterFile.getNumber("cpsr");
                   		int csprValue = RegisterFile.getValue(csprNum);
                   		int carry = csprValue & 0x20000000; // 29-bit means carry flag
                   		carry = carry << 2; // (C<<31)
                   		RegisterFile.updateRegister(operands[0], (carry | 
                   				RegisterFile.getValue(operands[1]) >>> 1));
                   }
                }));
/* For the collision with LSL operand in MOV, this thumb instruction could not be supported.
 * by Jeongho Nah 30th Dec. 2009
         instructionList.add(
                 new BasicInstruction("lsl r0,r1,#32",
             	 "(Thumb)Logical shift left(see MOV for the ARM equivalent)",
                 BasicInstructionFormat.I_FORMAT,
                 "0000 0000 0000 0000 00000 ttttt sss fff",
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      RegisterFile.updateRegister(operands[0],
                         RegisterFile.getValue(operands[1]) << operands[2]);
                   }
                }));
         instructionList.add(
                 new BasicInstruction("lsr r0,r1,#32",
                 "(Thumb)Logical shift left(see MOV for the ARM equivalent)",
                 BasicInstructionFormat.I_FORMAT,
                 "0000 0000 0000 0000 00001 ttttt sss fff",
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                   // must zero-fill, so use ">>>" instead of ">>".
                      RegisterFile.updateRegister(operands[0],
                         RegisterFile.getValue(operands[1]) >>> operands[2]);
                   }
                }));
*/
         instructionList.add(
                 new BasicInstruction("cmp r0,#-128",
                 "Compare <Rn, #<immed8>",
             	 BasicInstructionFormat.I_FORMAT,
                 "1110 00 1 1010 1 ffff 0000 ssssssssssss",
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                        int[] operands = statement.getOperands();
                        int sub1 = RegisterFile.getValue(operands[0]);
                        int sub2 = operands[1];
                        int dif = sub1 - sub2;
                        
                        int csprNum = RegisterFile.getNumber("cpsr");
                        int csprValue = RegisterFile.getValue(csprNum);
                        
                        if (dif == 0) {
                        	csprValue = (csprValue & 0x3fffffff); // reset 31, 30 bits
                        	csprValue = (csprValue | 0x40000000); // zero OR 01<30bit:0> 
                        }
                        else if (dif < 0) {
                        	csprValue = (csprValue & 0x3fffffff); // reset 31, 30 bits
                        	csprValue = (csprValue | 0x80000000); // negative OR 01<30bit:0>
                        }
                        else {
                        	csprValue = (csprValue & 0x3fffffff); // reset 31, 30 bits
                        }
                        RegisterFile.updateRegister(csprNum, csprValue);
                   }
                }));
         instructionList.add(
                 new BasicInstruction("cmp r0,r1",
                 "Compare Rn, Rd",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 00 0 1010 1 ffff 0000 ssssssssssss",
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                        int[] operands = statement.getOperands();
                        int sub1 = RegisterFile.getValue(operands[0]);
                        int sub2 = RegisterFile.getValue(operands[1]);
                        int dif = sub1 - sub2;
                        
                        int csprNum = RegisterFile.getNumber("cpsr");
                        int csprValue = RegisterFile.getValue(csprNum);
                        
                        if (dif == 0) {
                        	csprValue = (csprValue & 0x3fffffff); // reset 31, 30 bits
                        	csprValue = (csprValue | 0x40000000); // zero OR 01<30bit:0> 
                        }
                        else if (dif < 0) {
                        	csprValue = (csprValue & 0x3fffffff); // reset 31, 30 bits
                        	csprValue = (csprValue | 0x80000000); // negative OR 01<30bit:0>
                        }
                        else {
                        	csprValue = (csprValue & 0x3fffffff); // reset 31, 30 bits
                        }
                        RegisterFile.updateRegister(csprNum, csprValue);
                   }
                }));
         instructionList.add(
                 new BasicInstruction("and r0,r1,#-128",
             	 "Bitwise AND, #<immed8>",
             	BasicInstructionFormat.I_FORMAT,
                "1110 00 1 0000 0 ssss ffff tttttttttttt",
               //1110 00 1 0000 0 0001 0000 000000000111
               //and	r0, r1, #7
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      RegisterFile.updateRegister(operands[0],
                         RegisterFile.getValue(operands[1])
                         & operands[2]);
                   }
                }));
         instructionList.add(
                 new BasicInstruction("and r0,r1,r2",
             	 "Bitwise AND",
             	 BasicInstructionFormat.I_FORMAT,
                 "1110 00 0 0000 0 ssss ffff tttttttttttt",
                //1110 00 0 0000 0 0001 0000 000000000010
                //and	r0, r1, r2
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      RegisterFile.updateRegister(operands[0],
                         RegisterFile.getValue(operands[1])
                         & RegisterFile.getValue(operands[2]));
                   }
                }));
         instructionList.add(
                 new BasicInstruction("orr r0,r1,#-128",
             	 "Bitwise OR, #<immed8>",
             	 BasicInstructionFormat.I_FORMAT,
                 "1110 00 1 1100 0 ssss ffff tttttttttttt",
                //1110 00 1 1100 0 0001 0000 000000000111
                //orr r0, r1, #7
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      RegisterFile.updateRegister(operands[0],
                         RegisterFile.getValue(operands[1])
                         | operands[2]);
                   }
                }));
          instructionList.add(
                 new BasicInstruction("orr r0,r1,r2",
             	 "Bitwise OR",
             	 BasicInstructionFormat.I_FORMAT,
                 "1110 00 0 1100 0 ssss ffff tttttttttttt",
                //1110 00 0 1100 0 0001 0000 000000000010
                //orr r0, r1, r2
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      RegisterFile.updateRegister(operands[0],
                         RegisterFile.getValue(operands[1])
                         | RegisterFile.getValue(operands[2]));
                   }
                }));
         instructionList.add(
                 new BasicInstruction("ldr r0,[r1]",
             	 "Load word into register r0",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 01 0 1 1 0 0 1 ssss ffff 000000000000",
                //1110 01 0 1 1 0 0 1 0001 0000 000000000000
                //ldr	r0, [r1]
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      try
                      {
                         RegisterFile.updateRegister(operands[0],
                             Globals.memory.getWord(
                             RegisterFile.getValue(operands[1])));
                      } 
                          catch (AddressErrorException e)
                         {
                            throw new ProcessingException(statement, e);
                         }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("ldr r0,[r0,#0xFFFFF]",
             	 "Load word into register r0",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 01 0 1 1 0 0 1 ssss ffff tttttttttttt",
                //1110 01 0 1 1 0 0 1 1111 0000 000011111000
                //ldr	r0, [r1]
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      try
                      {
                         RegisterFile.updateRegister(operands[0],
                             Globals.memory.getWord(
                             RegisterFile.getValue(operands[1])+operands[2]));
                      } 
                          catch (AddressErrorException e)
                         {
                            throw new ProcessingException(statement, e);
                         }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("ldrb r0,[r1]",
                 "Load byte(zero-extend) into register r0",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 01 0 1 1 1 0 1 ssss ffff 000000000000",
                //1110 01 0 1 1 1 0 1 0001 0000 000000000000
                //ldrb	r0, [r1]
                 new SimulationCode()
                 {
                     public void simulate(ProgramStatement statement) throws ProcessingException
                    {
                       int[] operands = statement.getOperands();
                       try
                       {
                     	  int memValue = Globals.memory.getByte(RegisterFile.getValue(operands[1]));
                     	  memValue = memValue << 24;
                     	  memValue = memValue >>> 24;
                          RegisterFile.updateRegister(operands[0], memValue);
                       } 
                           catch (AddressErrorException e)
                          {
                             throw new ProcessingException(statement, e);
                          }
                    }
                 }));
         instructionList.add(
                 new BasicInstruction("ldrb r0,[r0,#0xFFFFF]",
             	 "Load word into register r0",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 01 0 1 1 0 0 1 ssss ffff tttttttttttt",
                //1110 01 0 1 1 0 0 1 1111 0000 000011111000
                //ldrb    r0, [pc, #32]
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      try
                      {
                         RegisterFile.updateRegister(operands[0],
                             Globals.memory.getByte(
                             RegisterFile.getValue(operands[1])+operands[2]));
                      } 
                          catch (AddressErrorException e)
                         {
                            throw new ProcessingException(statement, e);
                         }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("ldrsb r0,[r1]",
             	 "Load byte(sign-extend) into register r0",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 00 0 1 1 1 0 1 ssss ffff 0000 1101 tttt",
                //1110 00 0 1 1 1 0 1 0001 0000 0000 1101 0000
                 new SimulationCode()
                 {
                     public void simulate(ProgramStatement statement) throws ProcessingException
                    {
                       int[] operands = statement.getOperands();
                       try
                       {
                     	  int memValue = Globals.memory.getByte(RegisterFile.getValue(operands[1]));
                     	  memValue = memValue << 24;
                     	  memValue = memValue >> 24;
                          RegisterFile.updateRegister(operands[0], memValue);
                       } 
                           catch (AddressErrorException e)
                          {
                             throw new ProcessingException(statement, e);
                          }
                    }
                 }));
         instructionList.add(
                 new BasicInstruction("ldrsb r0,[r0,#0xFFFFF]",
             	 "Load byte(sign-extend) into register r0",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 00 0 1 1 1 0 1 ssss ffff tttt 1101 tttt",
                //1110 00 0 1 1 1 0 1 1111 0000 0001 1101 1100
                 new SimulationCode()
                 {
                     public void simulate(ProgramStatement statement) throws ProcessingException
                    {
                       int[] operands = statement.getOperands();
                       try
                       {
                     	  int memValue = Globals.memory.getByte(RegisterFile.getValue(operands[1])+operands[2]);
                     	  memValue = memValue << 24;
                     	  memValue = memValue >> 24;
                          RegisterFile.updateRegister(operands[0], memValue);
                       } 
                          catch (AddressErrorException e)
                          {
                             throw new ProcessingException(statement, e);
                          }
                    }
                 }));
         instructionList.add(
                 new BasicInstruction("ldrh r0,[r1]",
                 "Load Halfword(zero-extend) into register r0",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 00 0 1 1 1 0 1 ssss ffff 000010110000",
                //1110 00 0 1 1 1 0 1 0001 0000 000010110000
                //ldrh	r0, [r1]
                 new SimulationCode()
                 {
                     public void simulate(ProgramStatement statement) throws ProcessingException
                    {
                       int[] operands = statement.getOperands();
                       try
                       {
                     	  int memValue = Globals.memory.getHalf(RegisterFile.getValue(operands[1]));
                     	  memValue = memValue << 16;
                     	  memValue = memValue >>> 16;
                          RegisterFile.updateRegister(operands[0], memValue);
                       } 
                           catch (AddressErrorException e)
                          {
                             throw new ProcessingException(statement, e);
                          }
                    }
                 }));
         instructionList.add(
                 new BasicInstruction("ldrh r0,[r0,#0xFFFFF]",
                 "Load Halfword(zero-extend) into register r0",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 01 0 1 1 0 0 1 ssss ffff tttt 1011 tttt",
                //1110 00 0 1 1 1 0 1 1111 0000 0001 1011 1000
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      try
                      {
                         RegisterFile.updateRegister(operands[0],
                             Globals.memory.getHalf(
                             RegisterFile.getValue(operands[1])+operands[2]));
                      } 
                          catch (AddressErrorException e)
                         {
                            throw new ProcessingException(statement, e);
                         }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("ldrsh r0,[r1]",
             	 "Load Halfword(sign-extend) into register r0",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 00 0 1 1 1 0 1 ssss ffff 000000000000",
                //1110 00 0 1 1 1 0 1 0001 0000 000011110000
                //ldrsh	r0, [r1]
                 new SimulationCode()
                 {
                     public void simulate(ProgramStatement statement) throws ProcessingException
                    {
                       int[] operands = statement.getOperands();
                       try
                       {
                     	  int memValue = Globals.memory.getHalf(RegisterFile.getValue(operands[1]));
                     	  memValue = memValue << 16;
                     	  memValue = memValue >> 16;
                          RegisterFile.updateRegister(operands[0], memValue);
                       } 
                           catch (AddressErrorException e)
                          {
                             throw new ProcessingException(statement, e);
                          }
                    }
                 }));
         instructionList.add(
                 new BasicInstruction("ldrsh r0,[r0,#0xFFFFF]",
                 "Load Halfword(sign-extend) into register r0",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 00 0 1 1 1 0 1 ssss ffff tttt 1111 tttt",
                //1110 00 0 1 1 1 0 1 1111 0000 0001 1111 0100
                 new SimulationCode()
                 {
                     public void simulate(ProgramStatement statement) throws ProcessingException
                    {
                       int[] operands = statement.getOperands();
                       try
                       {
                     	  int memValue = Globals.memory.getHalf(RegisterFile.getValue(operands[1])+operands[2]);
                     	  memValue = memValue << 16;
                     	  memValue = memValue >> 16;
                          RegisterFile.updateRegister(operands[0], memValue);
                       } 
                           catch (AddressErrorException e)
                          {
                             throw new ProcessingException(statement, e);
                          }
                    }
                 }));
         instructionList.add(
                 new BasicInstruction("str r1,[r0]",
                 "Store word",
             	 BasicInstructionFormat.I_FORMAT,
             	 "1110 01 0 1 1 0 0 0 ssss ffff 000000000000",
                //1110 01 0 1 1 0 0 0 0001 0000 000000000000
             	//str	r0, [r1]
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      try
                      {
                         Globals.memory.setWord(
                             RegisterFile.getValue(operands[1]),
                             RegisterFile.getValue(operands[0]));
                      } 
                          catch (AddressErrorException e)
                         {
                            throw new ProcessingException(statement, e);
                         }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("strb r0,[r1]",
                 "Store byte",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 01 0 1 1 1 0 0 ssss ffff 000000000000",
                //1110 01 0 1 1 1 0 0 0001 0000 000000000000
                //strb	r0, [r1]
                 new SimulationCode()
                 {
                     public void simulate(ProgramStatement statement) throws ProcessingException
                    {
                       int[] operands = statement.getOperands();
                       try
                       {
                     	  int memValue = Globals.memory.getByte(RegisterFile.getValue(operands[1]));
                     	  int regValue = RegisterFile.getValue(operands[0]);
                     	  memValue = memValue & 0xFFFFFF00;
                     	  regValue = regValue << 24;
                     	  regValue = regValue >>> 24;
                     	  regValue = regValue | memValue;
                     	  
                     	 Globals.memory.setByte(RegisterFile.getValue(operands[1]), regValue);
                       } 
                           catch (AddressErrorException e)
                          {
                             throw new ProcessingException(statement, e);
                          }
                    }
                 }));
         instructionList.add(
                 new BasicInstruction("strh r0,[r1]",
                 "Store halfword",
                 BasicInstructionFormat.I_FORMAT,
                 "1110 00 0 1 1 1 0 0 ssss ffff 000010110000",
                //1110 00 0 1 1 1 0 0 0001 0000 000010110000
                //strh	r0, [r1]
                 new SimulationCode()
                 {
                     public void simulate(ProgramStatement statement) throws ProcessingException
                    {
                       int[] operands = statement.getOperands();
                       try
                       {
                     	  int memValue = Globals.memory.getHalf(RegisterFile.getValue(operands[1]));
                     	  int regValue = RegisterFile.getValue(operands[0]);
                     	  memValue = memValue & 0xFFFF0000;
                     	  regValue = regValue << 16;
                     	  regValue = regValue >>> 16;
                     	  regValue = regValue | memValue;
                     	  
                     	 Globals.memory.setHalf(RegisterFile.getValue(operands[1]), regValue);
                       }
                       catch (AddressErrorException e)
                       {
                    	   throw new ProcessingException(statement, e);
                       }
                    }
                 }));
         instructionList.add(
                 new BasicInstruction("b target",
                 "Branch <address25>",
             	 BasicInstructionFormat.I_BRANCH_FORMAT,
             	 "1110 101 0 ffffffffffffffffffffffff",
                //1110 101 0 111111111111111111111110
                //b label
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      processBranch(operands[0]);
                   }
                }));
         instructionList.add(
                 new BasicInstruction("bl target",
                 "Branch with possible state switch BL <address25>",
             	 BasicInstructionFormat.I_BRANCH_FORMAT,
             	 "1110 101 1 ffffffffffffffffffffffff",
                //1110 101 1 111111111111111111111110
                //bl label
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      if (operands[0] == Globals.addrPrintInt) {
                    	  // PRE-DEFINED: bl printInt
                    	  int addr_screen = RegisterFile.getValue(0);
                    	  String inputString = String.valueOf(RegisterFile.getValue(1));
                    	  try
                          {
                    		  for (int i=0; i<inputString.length(); ++i) {
                    			  Globals.memory.setByte(addr_screen + i, inputString.charAt(i));
                    		  }
                    		  RegisterFile.updateRegister(0, addr_screen+inputString.length()); // r0 contains the next address on the screen for printing
                          }
                          catch (AddressErrorException e)
                          {
                        	  throw new ProcessingException(statement, e);
                          }
                      }
                      else if (operands[0] == Globals.addrPrintCh) {
                    	  // PRE-DEFINED: bl printCh
                    	  int addr_screen = RegisterFile.getValue(0);
                    	  int value = RegisterFile.getValue(1);
                    	  try
                          {
                    		  Globals.memory.setByte(addr_screen, value);
                    		  RegisterFile.updateRegister(0, addr_screen+1); // r0 contains the next address on the screen for printing
                          }
                          catch (AddressErrorException e)
                          {
                        	  throw new ProcessingException(statement, e);
                          }
                      }
                      else {
                    	  processReturnAddress(14);// RegisterFile.updateRegister(31, RegisterFile.getProgramCounter());
                          processBranch(operands[0]);
                      }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("beq label",
                 "Branch if equal",
             	 BasicInstructionFormat.I_BRANCH_FORMAT,
             	 "0000 101 0 ffffffffffffffffffffffff",
                //0000 101 0 111111111111111111111110
                //beq label
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      int csprNum = RegisterFile.getNumber("cpsr");
                      int csprValue = RegisterFile.getValue(csprNum);
                   
                      if ((csprValue & 0x40000000) == 0x40000000)
                      {
                         processBranch(operands[0]);
                      }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("bne label",
                 "Branch if not equal",
                 BasicInstructionFormat.I_BRANCH_FORMAT,
             	 "0001 101 0 ffffffffffffffffffffffff",
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      int csprNum = RegisterFile.getNumber("cpsr");
                      int csprValue = RegisterFile.getValue(csprNum);
                   
                      if ((csprValue & 0x40000000) == 0)
                      {
                         processBranch(operands[0]);
                      }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("bge label",
                 "Branch greater than or equal",
             	 BasicInstructionFormat.I_BRANCH_FORMAT,
             	 "1010 101 0 ffffffffffffffffffffffff",
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      int csprNum = RegisterFile.getNumber("cpsr");
                      int csprValue = RegisterFile.getValue(csprNum);
                   
                      if (((csprValue & 0x40000000) == 0x40000000) || ((csprValue & 0x80000000) == 0))
                      {
                         processBranch(operands[0]);
                      }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("blt label",
                 "Branch less than",
             	 BasicInstructionFormat.I_BRANCH_FORMAT,
             	 "1011 101 0 ffffffffffffffffffffffff",
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      int csprNum = RegisterFile.getNumber("cpsr");
                      int csprValue = RegisterFile.getValue(csprNum);
                      int neg = (csprValue & 0x80000000);

                      if (((csprValue & 0x40000000) == 0) && ((csprValue & 0x80000000) == 0x80000000))
                      {
                         processBranch(operands[0]);
                      }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("bgt label",
                 "Branch greater than",
             	 BasicInstructionFormat.I_BRANCH_FORMAT,
             	 "1100 101 0 ffffffffffffffffffffffff",
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      int csprNum = RegisterFile.getNumber("cpsr");
                      int csprValue = RegisterFile.getValue(csprNum);
                   
                      if (((csprValue & 0x40000000) == 0) && ((csprValue & 0x80000000) == 0))
                      {
                         processBranch(operands[0]);
                      }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("ble label",
                 "Branch less than or equal",
             	 BasicInstructionFormat.I_BRANCH_FORMAT,
             	 "1101 101 0 ffffffffffffffffffffffff",
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                      int[] operands = statement.getOperands();
                      int csprNum = RegisterFile.getNumber("cpsr");
                      int csprValue = RegisterFile.getValue(csprNum);
                   
                      if (((csprValue & 0x40000000) == 0x40000000) || ((csprValue & 0x80000000) == 0x80000000))
                      {
                         processBranch(operands[0]);
                      }
                   }
                }));
         instructionList.add(
                 new BasicInstruction("swi #0xffffff",
             	 "Issue a system call",
             	BasicInstructionFormat.I_BRANCH_FORMAT,
             	"1110 1111 ffffffffffffffffffffffff",
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                    	int[] operands = statement.getOperands();
                    	findAndSimulateSyscall(operands[0],statement);
                   }
                }));
         instructionList.add(
                 new BasicInstruction("eor r0, r0, r0",
             	 "Logical exclusive OR of two 32-bit values",
             	BasicInstructionFormat.I_BRANCH_FORMAT,
             	"1110 1111 001 0 ssss ffff 0000 0 00 0 tttt",
             //  1110 0000 001 0 0001 0000 0000 0 00 0 0010
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                    	int[] operands = statement.getOperands();
                        int op1 = RegisterFile.getValue(operands[1]);
                        int op2 = RegisterFile.getValue(operands[2]);
                        int result = op1 ^ op2;                    
                        RegisterFile.updateRegister(operands[0], result);
                   }
                }));
         instructionList.add(
                 new BasicInstruction("eor r0, r1, #-128",
             	 "Logical exclusive OR of two 32-bit values",
             	BasicInstructionFormat.I_BRANCH_FORMAT,
             	"1110 1111 001 0 ssss ffff 0000 tttttttt",
             //  1110 0000 001 0 0001 0000 0000 00000010
                 new SimulationCode()
                {
                    public void simulate(ProgramStatement statement) throws ProcessingException
                   {
                    	int[] operands = statement.getOperands();
                        int op1 = RegisterFile.getValue(operands[1]);
                        int op2 = operands[2];
                        int result = op1 ^ op2;                    
                        RegisterFile.updateRegister(operands[0], result);
                   }
                }));

        ////////////// READ PSEUDO-INSTRUCTION SPECS FROM DATA FILE AND ADD //////////////////////
         addPseudoInstructions();
      	
        ////////////// GET AND CREATE LIST OF SYSCALL FUNCTION OBJECTS ////////////////////
         syscallLoader = new SyscallLoader();
         syscallLoader.loadSyscalls();
      	
        // Initialization step.  Create token list for each instruction example.  This is
        // used by parser to determine user program correct syntax.
         for (int i = 0; i < instructionList.size(); i++)
         {
            Instruction inst = (Instruction) instructionList.get(i);
            inst.createExampleTokenList();
         }
      
      }
   	
    /*  METHOD TO ADD PSEUDO-INSTRUCTIONS
    */
   
       private void addPseudoInstructions()
      {
         InputStream is = null;
         BufferedReader in = null;
         try
         {
            // leading "/" prevents package name being prepended to filepath.
            is = this.getClass().getResourceAsStream("/PseudoOps.txt");
            in = new BufferedReader(new InputStreamReader(is));
         } 
             catch (NullPointerException e)
            {
               System.out.println(
                    "Error: ARM9 pseudo-instruction file PseudoOps.txt not found.");
               System.exit(0);
            }
         try
         {
            String line, pseudoOp, template, firstTemplate, token;
            String description;
            StringTokenizer tokenizer;
            while ((line = in.readLine()) != null) {
                // skip over: comment lines, empty lines, lines starting with blank.
               if (!line.startsWith("#") && !line.startsWith(" ")
                        && line.length() > 0)  {  
                  description = "";
                  tokenizer = new StringTokenizer(line, "\t");
                  pseudoOp = tokenizer.nextToken();
                  template = "";
                  firstTemplate = null;
                  while (tokenizer.hasMoreTokens()) {
                     token = tokenizer.nextToken();
                     if (token.startsWith("#")) {  
                        // Optional description must be last token in the line.
                        description = token.substring(1);
                        break;
                     }
                     if (token.startsWith("COMPACT")) {
                        // has second template for Compact (16-bit) memory config -- added DPS 3 Aug 2009
                        firstTemplate = template;
                        template = "";
                        continue;
                     } 
                     template = template + token;
                     if (tokenizer.hasMoreTokens()) {
                        template = template + "\n";
                     }
                  }
                  ExtendedInstruction inst = (firstTemplate == null)
                         ? new ExtendedInstruction(pseudoOp, template, description)
                     	 : new ExtendedInstruction(pseudoOp, firstTemplate, template, description);
                  instructionList.add(inst);
						//if (firstTemplate != null) System.out.println("\npseudoOp: "+pseudoOp+"\ndefault template:\n"+firstTemplate+"\ncompact template:\n"+template);
               }
            }
            in.close();
         } 
             catch (IOException ioe)
            {
               System.out.println(
                    "Internal Error: ARM9 pseudo-instructions could not be loaded.");
               System.exit(0);
            } 
             catch (Exception ioe)
            {
               System.out.println(
                    "Error: Invalid ARM9 pseudo-instruction specification.");
               System.exit(0);
            }
      
      }
   	
    /**
     *  Given an operator mnemonic, will return the corresponding Instruction object(s)
     *  from the instruction set.  Uses straight linear search technique.
     *  @param name operator mnemonic (e.g. addi, sw,...)
     *  @return list of corresponding Instruction object(s), or null if not found.
     */
       public ArrayList matchOperator(String name)
      {
         ArrayList matchingInstructions = null;
        // Linear search for now....
         for (int i = 0; i < instructionList.size(); i++)
         {
            if (((Instruction) instructionList.get(i)).getName().compareToIgnoreCase(name)==0)
            {
               if (matchingInstructions == null) 
                  matchingInstructions = new ArrayList();
               matchingInstructions.add(instructionList.get(i));
            }
         }
         return matchingInstructions;
      
      }
   	
   	/*
   	 * Method to find and invoke a syscall given its service number.  Each syscall
   	 * function is represented by an object in an array list.  Each object is of
   	 * a class that implements Syscall or extends AbstractSyscall.
   	 */
   	 
       private void findAndSimulateSyscall(int number, ProgramStatement statement) 
                                                        throws ProcessingException {
         Syscall service = syscallLoader.findSyscall(number);         
         if (service != null) {
            service.simulate(statement);
            return;
         }
         throw new ProcessingException(statement,
              "invalid or unimplemented syscall service: " +
              number + " ", Exceptions.SYSCALL_EXCEPTION);
      }
   	
   	/*
   	 * Method to process a successful branch condition.  DO NOT USE WITH JUMP
   	 * INSTRUCTIONS!  The branch operand is a relative displacement in words
   	 * whereas the jump operand is an absolute address in bytes.
   	 *
   	 * The parameter is displacement operand from instruction.
   	 *
   	 * Handles delayed branching if that setting is enabled.
   	 */
   	 // 4 January 2008 DPS:  The subtraction of 4 bytes (instruction length) after
   	 // the shift has been removed.  It is left in as commented-out code below.
   	 // This has the effect of always branching as if delayed branching is enabled, 
   	 // even if it isn't.  This mod must work in conjunction with
   	 // ProgramStatement.java, buildBasicStatementFromBasicInstruction() method near
   	 // the bottom (currently line 194, heavily commented).
   	 
       private void processBranch(int displacement) { // ARM9 pc get 8 more values from current pc. Written by Jeongho Nah
         if (Globals.getSettings().getDelayedBranchingEnabled()) {
            // Register the branch target address (absolute byte address).
            DelayedBranch.register(RegisterFile.getProgramCounter()+4 + (displacement << 2));
         } 
         else {
            // Decrement needed because PC has already been incremented
            RegisterFile.setProgramCounter(
                RegisterFile.getProgramCounter()+4
                  + (displacement << 2)); // - Instruction.INSTRUCTION_LENGTH);	
         }	 
      }
   
   	/*
   	 * Method to process a jump.  DO NOT USE WITH BRANCH INSTRUCTIONS!  
   	 * The branch operand is a relative displacement in words
   	 * whereas the jump operand is an absolute address in bytes.
   	 *
   	 * The parameter is jump target absolute byte address.
   	 *
   	 * Handles delayed branching if that setting is enabled.
   	 */
   	 
       private void processJump(int targetAddress) {
         if (Globals.getSettings().getDelayedBranchingEnabled()) {
            DelayedBranch.register(targetAddress);
         } 
         else {
            RegisterFile.setProgramCounter(targetAddress);
         }	 
      }
   
   	/*
   	 * Method to process storing of a return address in the given
   	 * register.  This is used only by the "and link"
   	 * instructions: jal, jalr, bltzal, bgezal.  If delayed branching
   	 * setting is off, the return address is the address of the
   	 * next instruction (e.g. the current PC value).  If on, the
   	 * return address is the instruction following that, to skip over
   	 * the delay slot.
   	 *
   	 * The parameter is register number to receive the return address.
   	 */
   	 
       private void processReturnAddress(int register) {
         RegisterFile.updateRegister(register, RegisterFile.getProgramCounter() +
                 ((Globals.getSettings().getDelayedBranchingEnabled()) ? 
            	  Instruction.INSTRUCTION_LENGTH : 0) );	 
      }
   	
   }

